﻿namespace VisualPhysics.Scene.Core

open System.Drawing
open VisualPhysics.Scene.Core.Operators

/// The BallObject type represents a Ball.
type BallObject private (position: MVector<cm>, r: float<cm>, mass: float<gram>, color: Brush,
                            velocity: MVector<cm / s>, lastUpdate: float<s>, dummy: unit) =

    let carPosition = car position

    /// <summary>Creates a new ball with the given parameters.</summary>
    /// <param name="Position">The position in centimeters.</param>
    /// <param name="Radius">The radius in centimeters.</param>
    /// <param name="Mass">The mass in gram.</param>
    /// <param name="Color">The color as a System.Drawing.Brush.</param>
    /// <param name="Velocity">The velocity of the box.</param>
    /// <param name="LastUpdate">The time of the last update.</param>
    new (position, r, mass, ?color, ?velocity, ?lastUpdate) =
        let color = defaultArg color Brushes.Black
        let velocity = defaultArg velocity (0.0<cm / s>, 0.0)
        let lastUpdate = defaultArg lastUpdate 0.0<s>
        BallObject(position, r, mass, color, velocity, lastUpdate, ())
    
    /// The RectangleF containing the circle.
    member this.Rectangle =
        let diameter = (float32 (r / 1.0<cm>)) * 2.0f
        RectangleF(point (fst carPosition - r, snd carPosition - r), SizeF(diameter, diameter))

    interface Object with
        /// The tag of the ball - Ball
        member this.Tag = Ball
        /// The time of the last update.
        member this.LastSync = lastUpdate
        /// The position of the ball as a tuple in centimeters.
        member this.CarPosition = carPosition
        /// The position of the ball as an MVector of cm centimeters.
        member this.PolPosition = position
        /// The position of the ball on the X axis in centimeters.
        member this.X = fst carPosition
        /// The position of the ball on the Y axis in centimeters.
        member this.Y = snd carPosition
        /// The current velocity of the ball.
        member this.Velocity = velocity
        /// The color of the ball (System.Drawing.Brush).
        member this.Color = color
        /// The mass of the ball in gram.
        member this.Mass = mass
        /// <summary>Updates the ball's position and velocity to a given time with given set of forces.</summary>
        /// <param name="ForceSet">The AccelerationManager which will be used to calculate new velocity and position.</param>
        /// <returns>The updated ball.</returns>
        member this.Sync forceSet time =
            let dt = time - lastUpdate
            if dt <= 0.0<s> then
                this.Object()
            else
                let _velocity = forceSet.GetVelocity time velocity
                let _position = forceSet.GetPosition time velocity position
                BallObject(_position, r, mass, color, _velocity, time).Object()
        /// <summary>Returns a new ball with the new position.</summary>
        /// <param name="NewPosition">The new position as a tuple in centimeters.</param>
        /// <returns>The new ball as Object.</returns>
        member this.SetCarPosition _position =
            let _position = pol _position
            BallObject(_position, r, mass, color, velocity, lastUpdate).Object()
        /// <summary>Returns a new ball with the new position.</summary>
        /// <param name="NewPosition">The new position as an MVector of centimeters.</param>
        /// <returns>The new ball as Object.</returns>
        member this.SetPolPosition _position =
            BallObject(_position, r, mass, color, velocity, lastUpdate).Object()
        /// <summary>Returns a new ball with the new color.</summary>
        /// <param name="NewColor">The new color.</param>
        /// <returns>The new ball as Object.</returns>
        member this.SetColor _color =
            BallObject(position, r, mass, _color, velocity, lastUpdate).Object()
        /// <summary>Draws the ball using a given System.Drawing.Graphics.</summary>
        /// <param name="Graphics">The graphics which will be used to draw.</param>
        member this.Draw (g : Graphics) =
            g.FillEllipse(color, this.Rectangle)
    end

    // The size of the ball as a tuple in centimeters.
    member this.Radius = r

    /// Up-casted to an Object.
    member this.Object() = this :> Object